from flask import Blueprint, render_template_string, jsonify, current_app
import os
from collections import defaultdict
from app.utils.logger import FlinkLogger, log_api_call

bp = Blueprint("api_docs", __name__)

# API文档HTML模板
API_DOCS_TEMPLATE = """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flink Web Analytics API 文档</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f8f9fa;
        }
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px;
            border-radius: 10px;
            margin-bottom: 30px;
            text-align: center;
        }
        .header h1 {
            margin: 0;
            font-size: 2.5em;
        }
        .header p {
            margin: 10px 0 0 0;
            opacity: 0.9;
        }
        .section {
            background: white;
            padding: 25px;
            margin-bottom: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .section h2 {
            color: #2c3e50;
            border-bottom: 2px solid #3498db;
            padding-bottom: 10px;
            margin-top: 0;
        }
        .section h3 {
            color: #34495e;
            margin-top: 25px;
        }
        .endpoint {
            background: #f8f9fa;
            border-left: 4px solid #3498db;
            padding: 15px;
            margin: 15px 0;
            border-radius: 0 5px 5px 0;
        }
        .method {
            display: inline-block;
            padding: 4px 8px;
            border-radius: 4px;
            font-weight: bold;
            font-size: 0.9em;
            margin-right: 10px;
        }
        .get { background-color: #28a745; color: white; }
        .post { background-color: #007bff; color: white; }
        .put { background-color: #ffc107; color: black; }
        .delete { background-color: #dc3545; color: white; }
        .url {
            font-family: 'Courier New', monospace;
            background: #e9ecef;
            padding: 2px 6px;
            border-radius: 3px;
            font-size: 0.9em;
        }
        .description {
            margin: 10px 0;
            color: #6c757d;
        }
        .params {
            margin: 10px 0;
        }
        .param {
            margin: 5px 0;
            padding: 5px;
            background: #f8f9fa;
            border-radius: 3px;
        }
        .param-name {
            font-weight: bold;
            color: #495057;
        }
        .param-type {
            color: #6c757d;
            font-size: 0.9em;
        }
        .response {
            background: #e8f5e8;
            border: 1px solid #c3e6c3;
            border-radius: 5px;
            padding: 15px;
            margin: 10px 0;
        }
        .response pre {
            margin: 0;
            white-space: pre-wrap;
            font-size: 0.9em;
        }
        .error {
            background: #f8d7da;
            border: 1px solid #f5c6cb;
            color: #721c24;
            padding: 10px;
            border-radius: 5px;
            margin: 10px 0;
        }
        .nav {
            background: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            margin-bottom: 20px;
        }
        .nav h3 {
            margin-top: 0;
            color: #2c3e50;
        }
        .nav ul {
            list-style: none;
            padding: 0;
        }
        .nav li {
            margin: 5px 0;
        }
        .nav a {
            color: #3498db;
            text-decoration: none;
            padding: 5px 10px;
            border-radius: 3px;
            display: block;
            transition: background-color 0.2s;
        }
        .nav a:hover {
            background-color: #e3f2fd;
        }
        .status-codes {
            background: #fff3cd;
            border: 1px solid #ffeaa7;
            border-radius: 5px;
            padding: 15px;
            margin: 10px 0;
        }
        .code {
            font-weight: bold;
            color: #856404;
        }
        .footer {
            text-align: center;
            margin-top: 40px;
            padding: 20px;
            background: #f8f9fa;
            border-radius: 8px;
            color: #6c757d;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>🚀 Flink Web Analytics API</h1>
        <p>完整的作业管理API文档</p>
    </div>

    <div class="nav">
        <h3>📋 目录导航</h3>
        <ul>
            <li><a href="#basic-info">基础信息</a></li>
            <li><a href="#field-config">字段配置管理</a></li>
            <li><a href="#job-list">作业列表管理</a></li>
            <li><a href="#job-register">作业注册管理</a></li>
            <li><a href="#job-config">作业配置管理</a></li>
            <li><a href="#job-status">作业状态管理</a></li>
            <li><a href="#job-operations">作业操作</a></li>
            <li><a href="#bulk-operations">批量操作</a></li>
            <li><a href="#job-delete">作业删除</a></li>
            <li><a href="#error-responses">错误响应</a></li>
        </ul>
    </div>

    <div class="section" id="basic-info">
        <h2>📡 基础信息</h2>
        <p><strong>基础URL:</strong> <span class="url">http://localhost:8096/api/jobs</span></p>
        <p><strong>内容类型:</strong> <span class="url">application/json</span></p>
        <p><strong>字符编码:</strong> <span class="url">UTF-8</span></p>
    </div>

    <div class="section" id="response-spec">
        <h2>📦 统一响应规范</h2>
        <div class="response">
<pre>{
  "success": true,
  "message": "ok",
  "data": { /* 业务数据 */ }
}</pre>
        </div>
        <div class="error">
<pre>{
  "success": false,
  "error": "错误描述",
  "code": "ERROR_CODE"
}</pre>
        </div>
        <p class="description">所有接口遵循上述统一格式。对于未显式返回 <code>success</code> 的JSON，框架会通过 after_request 自动包装为统一格式。</p>
    </div>

    <div class="section" id="field-config">
        <h2>🔧 字段配置管理</h2>
        
        <div class="endpoint">
            <h3><span class="method get">GET</span> <span class="url">/field-config</span></h3>
            <div class="description">获取字段配置信息，包括所有可用字段和分组信息</div>
            <div class="response">
                <strong>响应示例:</strong>
                <pre>{
  "field_config": {
    "basic": [
      {"value": "job_name", "label": "作业名称", "required": true, "default": true}
    ]
  },
  "all_fields": [...],
  "group_titles": {
    "basic": "基础信息",
    "status": "状态信息"
  }
}</pre>
            </div>
        </div>

        <div class="endpoint">
            <h3><span class="method post">POST</span> <span class="url">/field-settings</span></h3>
            <div class="description">保存用户的字段设置（字段选择和排序）</div>
            <div class="params">
                <strong>请求体:</strong>
                <div class="param">
                    <span class="param-name">selected_fields</span> <span class="param-type">(array)</span> - 选中的字段列表
                </div>
                <div class="param">
                    <span class="param-name">field_order</span> <span class="param-type">(array)</span> - 字段排序
                </div>
                <div class="param">
                    <span class="param-name">user_id</span> <span class="param-type">(string)</span> - 用户ID
                </div>
            </div>
        </div>
    </div>

    <div class="section" id="api-catalog">
        <h2>📚 API 目录（自动生成）</h2>
        {% for group, rules in endpoints.items() %}
        <div class="endpoint">
            <h3>{{ group }}</h3>
            {% for item in rules %}
            <div class="param">
                <span class="method {{ item.method_class }}">{{ item.methods }}</span>
                <span class="url">{{ item.rule }}</span>
                {% if item.doc %}<div class="description">{{ item.doc }}</div>{% endif %}
            </div>
            {% endfor %}
        </div>
        {% endfor %}
    </div>

    <div class="section" id="job-register">
        <h2>📝 作业注册管理</h2>
        
        <div class="endpoint">
            <h3><span class="method post">POST</span> <span class="url">/register</span></h3>
            <div class="description">注册新作业到系统</div>
            <div class="params">
                <strong>请求体:</strong>
                <div class="param">
                    <span class="param-name">job_name</span> <span class="param-type">(string, 必填)</span> - 作业名称
                </div>
                <div class="param">
                    <span class="param-name">jar_path</span> <span class="param-type">(string, 必填)</span> - JAR包路径
                </div>
                <div class="param">
                    <span class="param-name">entry_class</span> <span class="param-type">(string, 必填)</span> - 入口类
                </div>
                <div class="param">
                    <span class="param-name">program_args</span> <span class="param-type">(string)</span> - 程序参数
                </div>
                <div class="param">
                    <span class="param-name">auto_recovery</span> <span class="param-type">(int)</span> - 自动恢复 (0/1)
                </div>
            </div>
        </div>
    </div>

    <div class="section" id="job-operations">
        <h2>⚡ 作业操作</h2>
        
        <div class="endpoint">
            <h3><span class="method post">POST</span> <span class="url">/{job_name}/stop</span></h3>
            <div class="description">停止单个作业，支持重试机制和状态检查</div>
            <div class="params">
                <strong>路径参数:</strong>
                <div class="param">
                    <span class="param-name">job_name</span> <span class="param-type">(string)</span> - 作业名称
                </div>
                <strong>请求体:</strong>
                <div class="param">
                    <span class="param-name">flink_env</span> <span class="param-type">(string, 可选)</span> - Flink环境URL
                </div>
                <div class="param">
                    <span class="param-name">savepoint</span> <span class="param-type">(boolean)</span> - 是否创建保存点
                </div>
            </div>
        </div>

        <div class="endpoint">
            <h3><span class="method post">POST</span> <span class="url">/{job_pk}/restart</span></h3>
            <div class="description">重启作业，先停止当前作业，等待停止成功后启动新作业</div>
            <div class="params">
                <strong>路径参数:</strong>
                <div class="param">
                    <span class="param-name">job_pk</span> <span class="param-type">(int)</span> - 作业主键ID
                </div>
                <strong>请求体:</strong>
                <div class="param">
                    <span class="param-name">program_args</span> <span class="param-type">(string)</span> - 程序参数
                </div>
                <div class="param">
                    <span class="param-name">flink_env</span> <span class="param-type">(string, 可选)</span> - Flink环境URL
                </div>
            </div>
        </div>
    </div>

    <div class="section" id="bulk-operations">
        <h2>🔄 批量操作</h2>
        
        <div class="endpoint">
            <h3><span class="method post">POST</span> <span class="url">/bulk-stop</span></h3>
            <div class="description">批量停止作业，支持重试机制和详细结果返回，串行处理</div>
            <div class="params">
                <strong>请求体:</strong>
                <div class="param">
                    <span class="param-name">ids</span> <span class="param-type">(array)</span> - 作业ID列表
                </div>
                <div class="param">
                    <span class="param-name">interval</span> <span class="param-type">(int)</span> - 作业间间隔（秒）
                </div>
                <div class="param">
                    <span class="param-name">flink_env</span> <span class="param-type">(string, 可选)</span> - Flink环境URL
                </div>
            </div>
        </div>

        <div class="endpoint">
            <h3><span class="method post">POST</span> <span class="url">/bulk-restart</span></h3>
            <div class="description">批量重启作业，排队处理，支持默认参数和统一参数两种模式</div>
            <div class="params">
                <strong>请求体:</strong>
                <div class="param">
                    <span class="param-name">job_names</span> <span class="param-type">(array)</span> - 作业名称列表
                </div>
                <div class="param">
                    <span class="param-name">mode</span> <span class="param-type">(string)</span> - 模式：default/unified
                </div>
                <div class="param">
                    <span class="param-name">unified_args</span> <span class="param-type">(string)</span> - 统一参数（unified模式）
                </div>
            </div>
        </div>
    </div>

    <div class="section" id="error-responses">
        <h2>❌ 错误响应</h2>
        <p>所有API在出错时都会返回以下格式的错误响应：</p>
        <div class="response">
            <pre>{
  "error": "错误描述信息"
}</pre>
        </div>
        
        <div class="status-codes">
            <strong>常见HTTP状态码:</strong>
            <div class="param">
                <span class="code">200</span> - 成功
            </div>
            <div class="param">
                <span class="code">400</span> - 请求参数错误
            </div>
            <div class="param">
                <span class="code">404</span> - 资源不存在
            </div>
            <div class="param">
                <span class="code">500</span> - 服务器内部错误
            </div>
        </div>
    </div>

    <div class="section">
        <h2>💡 注意事项</h2>
        <ul>
            <li><strong>Flink环境参数:</strong> 所有支持<code>flink_env</code>参数的API都可以指定不同的Flink集群环境</li>
            <li><strong>重试机制:</strong> 停止和重启操作都支持重试机制，提高操作成功率</li>
            <li><strong>串行处理:</strong> 批量操作采用串行处理，避免对Flink集群造成过大压力</li>
            <li><strong>状态验证:</strong> 停止操作后会验证作业是否真的停止</li>
            <li><strong>监控状态:</strong> 系统会自动计算和更新作业的监控状态</li>
        </ul>
    </div>

    <div class="footer">
        <p>📚 Flink Web Analytics API 文档 v1.2.0</p>
        <p>最后更新: {{ current_time }}</p>
    </div>
</body>
</html>
"""

@bp.route("/")
@log_api_call("api_docs.api_docs")
def api_docs():
    """API文档页面"""
    current_app.logger.info("Generating API documentation page")
    from datetime import datetime
    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    # 动态收集端点
    url_map = current_app.url_map
    groups = defaultdict(list)
    method_order = ["GET","POST","PUT","DELETE","PATCH"]

    for rule in url_map.iter_rules():
        rule_str = str(rule)
        if not rule_str.startswith("/api/"):
            continue
        if rule.endpoint.startswith("static"):
            continue
        methods = sorted([m for m in rule.methods if m not in ("HEAD","OPTIONS")], key=lambda m: method_order.index(m) if m in method_order else 99)
        if not methods:
            continue
        view = current_app.view_functions.get(rule.endpoint)
        doc = (view.__doc__ or "").strip() if view else ""
        group = rule_str.split("/")[2] if len(rule_str.split("/"))>2 else "misc"
        item = {
            "rule": rule_str,
            "methods": ", ".join(methods),
            "method_class": methods[0].lower(),
            "endpoint": rule.endpoint,
            "doc": doc,
        }
        groups[group].append(item)

    # 每组内按路径排序
    for g in groups:
        groups[g] = sorted(groups[g], key=lambda i: i["rule"]) 

    return render_template_string(API_DOCS_TEMPLATE, current_time=current_time, endpoints=groups)

@bp.route("/json")
@log_api_call("api_docs.api_docs_json")
def api_docs_json():
    """API文档JSON格式（自动生成）"""
    current_app.logger.info("Generating API documentation in JSON format")
    url_map = current_app.url_map
    items = []
    for rule in url_map.iter_rules():
        rule_str = str(rule)
        if not rule_str.startswith("/api/"):
            continue
        methods = [m for m in rule.methods if m not in ("HEAD","OPTIONS")]
        if not methods:
            continue
        view = current_app.view_functions.get(rule.endpoint)
        doc = (view.__doc__ or "").strip() if view else ""
        items.append({
            "path": rule_str,
            "methods": methods,
            "endpoint": rule.endpoint,
            "doc": doc
        })
    return jsonify({
        "title": "Flink Web Analytics API",
        "version": "1.2.0",
        "base_url": "http://localhost:8096/api",
        "response_spec": {
            "success": {"success": True, "message": "ok", "data": {"...": "..."}},
            "error": {"success": False, "error": "描述", "code": "ERROR_CODE"}
        },
        "endpoints": sorted(items, key=lambda x: x["path"]) 
    })
